package top.ieei.demo.tftp;

/**
 * 
 * TFTP选型确认响应包<br>
 * @author xiecongshu
 */
public class TftpOptionAck extends TftpAck
{
    
    public static final int OPCODE = 6;
    
    /**
     * 存储TFTP选项信息
     */
    private Options options = null;
    
    /**
     * <默认构造函数>
     */
    public TftpOptionAck(int blockNum, Options options)
    {
        super(blockNum);
        this.options = options;
    }
    
    /**
     * 是否有超时选项
     * @return boolean
     */
    public boolean hasTimeout()
    {
        if (null != options)
        {
            return options.hasOption(Options.TIMEOUT);
        }
        return false;
    }
    
    /**
     * 获取超时时间
     * @return int
     */
    public int getTimeout()
    {
        if (null != options)
        {
            return options.getTimeout();
        }
        return -1;
    }
    
    /**
     * 是否有文件传输大小选项
     * @return boolean
     */
    public boolean hasTransferSize()
    {
        if (null != options)
        {
            return options.hasOption(Options.TSIZE);
        }
        return false;
    }
    
    /**
     * 获取传输文件大小
     * @return int
     */
    public int getTransferSize()
    {
        if (null != options)
        {
            return options.getTransferSize();
        }
        return -1;
    }
    
    /**
     * 读取TFTP选项数据，返回下个选项索引位置
     * 
     * @param optionIndex 开始读取选项的索引
     * @param b 包含TFTP请求的包的字节数组
     * @return 返回下个选项索引位置
     * @throws TftpException
     */
    private int readOption(int optionIndex, byte[] b) throws TftpException
    {
        // 查是否会造成数组越界读取
        if (optionIndex >= b.length)
        {
            return optionIndex;
        }
        
        StringBuffer sb = new StringBuffer();
        int valueIndex = readStringFromByteArray(optionIndex, b, sb);
        String option = sb.toString();
        sb = null;
        
        // 查是否会造成数组越界读取
        if (valueIndex >= b.length)
        {
            throw new TftpException(
                    "TFTP packet passed to constructor is not a complete packet! Missing value for option: "
                            + option);
        }
        
        sb = new StringBuffer();
        optionIndex = readStringFromByteArray(valueIndex, b, sb);
        String value = sb.toString();
        sb = null;
        
        // 检查是否还没有实例化选项对象
        if (options == null)
        {
            options = new Options(3);
        }
        
        options.put(option, value);
        return optionIndex;
    }
    
    /**
     * <默认构造函数>
     * @param tftpBytes
     * @throws TftpException
     */
    public TftpOptionAck(byte[] tftpBytes) throws TftpException
    {
        super(tftpBytes);
        int optionIndex = INDEX_OPCODE + 2;
        
        while (optionIndex < tftpBytes.length - 2)
        {
            optionIndex = readOption(optionIndex, tftpBytes);
        }
        blockNum = 0;
    }
    
    /**
     * 获取选项确认响应包数据的长度
     * @return int
     */
    private int getLength()
    {
        int length = 4;
        if (options == null)
            return length;
        int timeout = options.getTimeout();
        if (timeout > 0)
        {
            length += Options.TIMEOUT.length()
                    + ((String) options.get(Options.TIMEOUT)).length() + 2;
        }
        
        int tsize = options.getTransferSize();
        if (tsize > 0)
        {
            length += Options.TSIZE.length()
                    + ((String) options.get(Options.TSIZE)).length() + 2;
        }
        
        int blksize = options.getBlockSize();
        if (blksize > 512)
        {
            length += Options.BLKSIZE.length()
                    + ((String) options.get(Options.BLKSIZE)).length() + 2;
        }
        return length;
    }
    
    /**
     * 获取响应数据的字节数组
     * @return  byte[]
     */
    public byte[] getBytes()
    {
        byte[] tftpBytes = new byte[getLength()];
        
        // 加入两字节的操作码
        tftpBytes[INDEX_OPCODE] = (byte) ((OPCODE >> 8) & 0xff);
        tftpBytes[INDEX_OPCODE + 1] = (byte) (OPCODE & 0xff);
        
        int optionIndex = INDEX_OPCODE + 2;
        int timeout = options.getTimeout();
        if (timeout > 0)
        {
            int length = Options.TIMEOUT.length();
            System.arraycopy(Options.TIMEOUT.getBytes(),
                    0,
                    tftpBytes,
                    optionIndex,
                    length);
            optionIndex += length;
            tftpBytes[optionIndex++] = 0;
            
            String timeoutValue = (String) options.get(Options.TIMEOUT);
            length = timeoutValue.length();
            System.arraycopy(timeoutValue.getBytes(),
                    0,
                    tftpBytes,
                    optionIndex,
                    length);
            optionIndex += length;
            tftpBytes[optionIndex++] = 0;
        }
        
        int tsize = options.getTransferSize();
        if (tsize > 0)
        {
            int length = Options.TSIZE.length();
            System.arraycopy(Options.TSIZE.getBytes(),
                    0,
                    tftpBytes,
                    optionIndex,
                    length);
            optionIndex += length;
            tftpBytes[optionIndex++] = 0;
            
            String tsizeValue = (String) options.get(Options.TSIZE);
            length = tsizeValue.length();
            System.arraycopy(tsizeValue.getBytes(),
                    0,
                    tftpBytes,
                    optionIndex,
                    length);
            optionIndex += length;
            tftpBytes[optionIndex++] = 0;
        }
        
        int blksize = options.getBlockSize();
        if (blksize > 512)
        {
            int length = Options.BLKSIZE.length();
            System.arraycopy(Options.BLKSIZE.getBytes(),
                    0,
                    tftpBytes,
                    optionIndex,
                    length);
            optionIndex += length;
            tftpBytes[optionIndex++] = 0;
            
            String blksizeValue = (String) options.get(Options.BLKSIZE);
            length = blksizeValue.length();
            System.arraycopy(blksizeValue.getBytes(),
                    0,
                    tftpBytes,
                    optionIndex,
                    length);
            optionIndex += length;
            tftpBytes[optionIndex++] = 0;
        }
        
        int blkNumberIndex = optionIndex;
        
        // 加入两字节的块编号
        tftpBytes[blkNumberIndex] = (byte) ((blockNum >> 8) & 0xff);
        tftpBytes[blkNumberIndex + 1] = (byte) (blockNum & 0xff);
        return tftpBytes;
    }
    
    /**
     * 获取操作码
     * @return int
     */
    public int getOpCode()
    {
        return OPCODE;
    }
}
